/**
 * 
 */
package prv.pursuivant.armory.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.transform.Result;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import prv.pursuivant.armory.DOMParser;
import prv.pursuivant.core.Achievement;
import prv.pursuivant.core.Toon;
import prv.pursuivant.core.ToonAchievements;
import prv.pursuivant.core.impl.AchievementImpl;
import prv.pursuivant.core.impl.ToonAchievementsImpl;
import prv.pursuivant.util.DateUtil;

import com.google.inject.Inject;

/**
 * @author Kwinto
 * 
 */
public class DOMParserImpl implements DOMParser {

	private final DateUtil dateParser;

	/**
	 * 
	 * @param category
	 */
	@Inject
	public DOMParserImpl(DateUtil dateParser) {
		this.dateParser = dateParser;
	}

	/**
	 * @see prv.pursuivant.armory.DOMParser#parseDOM(prv.pursuivant.core.impl.ToonImpl,
	 *      org.w3c.dom.Document)
	 */
	@Override
	public ToonAchievements parseDOM(Toon toon, Document dom) {
		NodeList achievementList = dom.getElementsByTagName("achievement");
		List<Achievement> result = new ArrayList<Achievement>(achievementList
				.getLength());
		List<String> markedNames = new ArrayList<String>();
		Map<String, String> knownIds = new HashMap<String, String>();

		// two passes: first read only <achievements>

		for (int i = 0; i < achievementList.getLength(); i++) {
			final Element achievementElem = (Element) achievementList.item(i);
			final String id = achievementElem.getAttribute("id");
			final String title = achievementElem.getAttribute("title");
			boolean completed = achievementElem.hasAttribute("dateCompleted");
			if (completed) {
				Date dateCompleted = dateParser.parseWowDate(achievementElem
						.getAttribute("dateCompleted"));
				result.add(new AchievementImpl(toon, dateCompleted, id));
				markedNames.add(title);
			}
			knownIds.put(title, id);
		}

		// then read all <criterias> that aren't <achievements> on their own.

		for (int i = 0; i < achievementList.getLength(); i++) {
			final Element achievementElem = (Element) achievementList.item(i);
			final String id = achievementElem.getAttribute("id");
			boolean completed = achievementElem.hasAttribute("dateCompleted");
			Date dateCompleted = completed ? dateParser
					.parseWowDate(achievementElem.getAttribute("dateCompleted"))
					: null;
			NodeList criteriaList = achievementElem
					.getElementsByTagName("criteria");
			for (int j = 0; j < criteriaList.getLength(); j++) {
				final Element criteriaElem = (Element) criteriaList.item(j);
				final String criteriaName = criteriaElem.getAttribute("name");
				if (criteriaName.isEmpty()) // skip "quantity" criteria
					continue;
				String critId = knownIds.containsKey(criteriaName) ? knownIds
						.get(criteriaName) : id + "." + criteriaName;

				boolean completedCrit = criteriaElem.hasAttribute("date");
				// if criteria are completed, date is taken from completion
				// date, otherwise from achievement completion time
				Date date = completedCrit ? dateParser
						.parseWowDate(criteriaElem.getAttribute("date"))
						: dateCompleted;
				if (date != null && !markedNames.contains(criteriaName)) {
					result.add(new AchievementImpl(toon, date, critId));
				}
			}
		}
		if (result.isEmpty()) { // TODO: do something with that!
			// parseDOM(toon, dom);
			try {
				TransformerFactory.newInstance().newTransformer().transform(
						new DOMSource(dom), new StreamResult(System.err));
			} catch (RuntimeException e) {
				throw e;
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			throw new RuntimeException("Toon " + toon.getName()
					+ " has empty achievement list");
		}
		final Map<String, Achievement> map = new HashMap<String, Achievement>(
				result.size());
		for (Achievement achiev : result)
			map.put(achiev.getId(), achiev);
		return new ToonAchievementsImpl(map);
	}

}
